﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Input;
using System.Windows.Threading;
using Azukit.WPF.Command;
using Azukit.WPF.ViewModel;
using Hibiku.Models;
using Hibiku.Services;
using Hibiku.Twitter;
using Hibiku.Views;

namespace Hibiku.ViewModels
{
    public class UserStreamViewModel : ViewAwareViewModelBase
    {
        public ICommand EditFiltersCommand
        {
            get;
            private set;
        }

        public ICommand GetHomeTimelineCommand
        {
            get;
            private set;
        }

        public Action<string, Location> Tweeter
        {
            get;
            private set;
        }

        public LogViewModel Log
        {
            get;
            private set;
        }

        public ObservableCollection<TweetViewModel> HomeTimelineTweets
        {
            get;
            private set;
        }

        public ObservableCollection<TweetViewModel> MentionTweets
        {
            get;
            private set;
        }

        private readonly ITwitterApi m_api;
        private readonly IApplicationConfigurationService m_configurationService;
        private readonly IFeatureTrackingService m_featureTrackingService;

        public UserStreamViewModel(ITwitterApi api, 
            IApplicationConfigurationService configurationService, 
            IFeatureTrackingService featureTrackingService)
        {
            m_api = api;
            m_api.TweetsReceived += HandleTweetsReceived;
            m_api.Unauthorized += HandleUnauthorized;
            m_api.Authorized += HandleAuthorized;

            m_configurationService = configurationService;
            m_configurationService.ConfigurationSaved += HandleConfigurationSaved;

            m_featureTrackingService = featureTrackingService;

            HomeTimelineTweets = new ObservableCollection<TweetViewModel>();
            MentionTweets = new ObservableCollection<TweetViewModel>();
            Log = new LogViewModel();

            EditFiltersCommand = new DelegatingCommand(x => DoEditFilters());
            GetHomeTimelineCommand = new DelegatingCommand(x => DoGetHomeTimeline(), x => m_api.IsAuthorized);
            Tweeter = SendTweet;

            UpdateDisplayName();
            StartRefreshTimer();

            if (m_api.IsAuthorized)
            {
                DoGetMentions();
                DoGetHomeTimeline();
            }
            else
            {
                DoAuthorize();
            }
        }

        private void HandleAuthorized(object sender, EventArgs e)
        {
            m_configurationService.Configuration.Authorization = m_api.Authorization;
            m_configurationService.Save();

            Log.Info("Authorized as '{0}'", m_api.Authorization.UserName);
            AddHighlightRuleUnlessExists(m_api.Authorization.UserName);
            UpdateDisplayName();

            DoGetHomeTimeline();
            DoGetMentions();

            CommandManager.InvalidateRequerySuggested();
        }

        private void HandleUnauthorized(object sender, EventArgs e)
        {
            DoAuthorize();
        }

        private void DoAuthorize()
        {
            new AuthorizationView().ShowDialog();
        }

        private void StartRefreshTimer()
        {
            var timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 5, 0);
            timer.Tick += (s, e) =>
            {
                if (m_api.IsAuthorized)
                {
                    DoGetMentions();
                    DoGetHomeTimeline();
                }
            };
            timer.Start();
        }

        private void UpdateDisplayName()
        {
            if (m_api.IsAuthorized)
            {
                DisplayName = m_api.Authorization.UserName;
            }
            else
            {
                DisplayName = "(Unauthorized)";
            }
        }

        private void DoEditFilters()
        {
            m_featureTrackingService.TrackFeature("Filters.Edit");

            new FilterEditView().Show();
        }

        private void HandleConfigurationSaved(object sender, EventArgs e)
        {
            foreach (var model in HomeTimelineTweets)
            {
                var action = GetFilterMatchAction(model.Tweet);
                model.IsHighlighted = (action != null && action.Value == EFilterMatchAction.Highlight);
            }
        }

        private void HandleTweetsReceived(EStream stream, IEnumerable<Tweet> tweets)
        {
            var collection = (stream == EStream.Mentions ? MentionTweets : HomeTimelineTweets);

            foreach (var tweet in tweets.OrderBy(x => x.CreatedAt.Ticks))
            {
                var action = GetFilterMatchAction(tweet);

                if (action.HasValue && action.Value == EFilterMatchAction.Highlight)
                {
                    collection.Insert(0, new TweetViewModel(tweet, true, m_api));
                }
                else if (!action.HasValue)
                {
                    collection.Insert(0, new TweetViewModel(tweet, false, m_api));
                }
            }

            if (tweets.Count() > 0)
            {
                Log.Info("{0}: Loaded {1} tweet(s).", stream, tweets.Count());

                m_featureTrackingService.Log("{0}: Loaded {1} tweet(s).", stream, tweets.Count());
            }
        }

        private void AddHighlightRuleUnlessExists(string pattern)
        {
            if (!m_configurationService.Configuration.FilterRules.Any(x => x.Pattern == pattern))
            {
                m_configurationService.Configuration.FilterRules.Add(new FilterRuleModel(pattern, EFilterMatchAction.Highlight));
                m_configurationService.Save();
            }
        }

        private void DoGetHomeTimeline()
        {
            try
            {
                var lastTweetId = (HomeTimelineTweets.Count == 0 ? (long?)null : HomeTimelineTweets.Max(x => x.Tweet.Id));

                m_api.GetTweets(EStream.HomeTimeline, lastTweetId);
            }
            catch (Exception ex)
            {
                Log.Error("Failed to retrieve timeline: {0}", ex.Message);

                m_featureTrackingService.TrackException(ex, "Timeline.Get");
            }
        }

        private void DoGetMentions()
        {
            try
            {
                var lastTweetId = (MentionTweets.Count == 0 ? (long?)null : MentionTweets.Max(x => x.Tweet.Id));

                m_api.GetTweets(EStream.Mentions, lastTweetId);
            }
            catch (Exception ex)
            {
                Log.Error("Failed to retrieve mentions: {0}", ex.Message);

                m_featureTrackingService.TrackException(ex, "Mentions.Get");
            }
        }

        private EFilterMatchAction? GetFilterMatchAction(Tweet tweet)
        {
            foreach (var filterRule in m_configurationService.Configuration.FilterRules)
            {
                if (Regex.IsMatch(tweet.Text, filterRule.Pattern))
                {
                    return filterRule.Action;
                }
            }

            return null;
        }

        private void SendTweet(string text, Location location)
        {
            try
            {
                m_featureTrackingService.TrackFeature("Tweet.Send");

                m_api.Update(text, null, location);

                Log.Info("Tweeted '{0}'", text);
            }
            catch (Exception ex)
            {
                Log.Error("Failed to send tweet: {0}", ex.Message);

                m_featureTrackingService.TrackException(ex, "Tweet.Send");
            }
        }
    }
}